package com.harvest.scientific.ladder.util

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.io.Reader
import java.lang.reflect.Type
import java.util.concurrent.ConcurrentHashMap

class GsUtil private constructor() {

    init {
        throw UnsupportedOperationException("u can't instantiate me...")
    }

    companion object {

        private val TAG = GsUtil::class.java.getSimpleName()
        private val GSON = createGson(true)
        private val GSON_NO_NULLS = createGson(false)
        private val GSONS: ConcurrentHashMap<String, Gson> = ConcurrentHashMap()
        private const val KEY_LOG_UTILS = "logUtilsGson"

        val gson: Gson
            get() = getGson(true)

        fun getGson(serializeNulls: Boolean): Gson {
            return if (serializeNulls) GSON_NO_NULLS else GSON
        }

        @JvmStatic
        fun getGson4LogUtils(): Gson? {
            var gson4LogUtils = GSONS[KEY_LOG_UTILS]
            if (gson4LogUtils == null) {
                gson4LogUtils = GsonBuilder().setPrettyPrinting().serializeNulls().create()
                GSONS[KEY_LOG_UTILS] = gson4LogUtils
            }
            return gson4LogUtils
        }

        fun toJson(`object`: Any?): String? {
            return try {
                toJson(`object`, true)
            } catch (e: Exception) {
                null
            }
        }

        fun toJson(`object`: Any?, includeNulls: Boolean): String? {
            return try {
                if (includeNulls) GSON.toJson(`object`) else GSON_NO_NULLS.toJson(`object`)
            } catch (e: Exception) {
                null
            }
        }

        fun toJson(src: Any?, typeOfSrc: Type?): String? {
            return try {
                toJson(src, typeOfSrc, true)
            } catch (e: Exception) {
                null
            }
        }

        fun toJson(src: Any?, typeOfSrc: Type?, includeNulls: Boolean): String? {
            return try {
                if (includeNulls) GSON.toJson(src, typeOfSrc) else GSON_NO_NULLS.toJson(
                    src,
                    typeOfSrc
                )
            } catch (e: Exception) {
                null
            }
        }

        fun <T> fromJson(json: String?, type: Class<T>?): T? {
            try {
                return GSON.fromJson(json, type)
            } catch (e: Exception) {
            }
            return null
        }

        fun <T> fromJsonNoCatch(json: String?, type: Class<T>?): T {
            return GSON.fromJson(json, type)
        }

        fun <T> fromJson(json: String?, type: Type?): T? {
            try {
                return GSON.fromJson(json, type)
            } catch (e: Exception) {
            }
            return null
        }

        fun <T> fromJson(reader: Reader?, type: Class<T>?): T? {
            try {
                return GSON.fromJson(reader, type)
            } catch (e: Exception) {
            }
            return null
        }

        fun <T> fromJson(reader: Reader?, type: Type?): T? {
            try {
                return GSON.fromJson(reader, type)
            } catch (e: Exception) {
            }
            return null
        }

        fun getListType(type: Type?): Type {
            return TypeToken.getParameterized(MutableList::class.java, type).type
        }

        fun getSetType(type: Type?): Type {
            return TypeToken.getParameterized(MutableSet::class.java, type).type
        }

        fun getMapType(keyType: Type?, valueType: Type?): Type {
            return TypeToken.getParameterized(MutableMap::class.java, keyType, valueType).type
        }

        fun getArrayType(type: Type?): Type {
            return TypeToken.getArray(type).type
        }

        fun getType(rawType: Type?, vararg typeArguments: Type?): Type {
            return TypeToken.getParameterized(rawType, *typeArguments).type
        }

        private fun createGson(serializeNulls: Boolean): Gson {
            val builder = GsonBuilder()
            if (serializeNulls) {
                builder.serializeNulls()
            }
            return builder.create()
        }
    }
}
